
<!DOCTYPE html>

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />

    <title>A Miscellany of Leo Scripting &#8212; Leo 6.7.2 documentation</title>
    <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
    <link rel="stylesheet" type="text/css" href="_static/classic.css" />
    <link rel="stylesheet" type="text/css" href="_static/custom.css" />
    
    <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
    <script src="_static/jquery.js"></script>
    <script src="_static/underscore.js"></script>
    <script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
    <script src="_static/doctools.js"></script>
    <script src="_static/sphinx_highlight.js"></script>
    
    <script src="_static/sidebar.js"></script>
    
    <link rel="index" title="Index" href="genindex.html" />
    <link rel="search" title="Search" href="search.html" />
    <link rel="next" title="Exploring Leo’s Code Base" href="theory.html" />
    <link rel="prev" title="Leo’s Console Gui" href="console-gui.html" /> 
  </head><body>
    <div class="related" role="navigation" aria-label="related navigation">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="genindex.html" title="General Index"
             accesskey="I">index</a></li>
        <li class="right" >
          <a href="theory.html" title="Exploring Leo’s Code Base"
             accesskey="N">next</a> |</li>
        <li class="right" >
          <a href="console-gui.html" title="Leo’s Console Gui"
             accesskey="P">previous</a> |</li>
        <li class="nav-item nav-item-0"><a href="leo_toc.html">Leo 6.7.2 documentation</a> &#187;</li>
          <li class="nav-item nav-item-1"><a href="intermediatetopics.html" accesskey="U">Advanced Topics</a> &#187;</li>
        <li class="nav-item nav-item-this"><a href="">A Miscellany of Leo Scripting</a></li> 
      </ul>
    </div>  

    <div class="document">
      <div class="documentwrapper">
        <div class="bodywrapper">
          <div class="body" role="main">
            
  <section id="a-miscellany-of-leo-scripting">
<h1>A Miscellany of Leo Scripting<a class="headerlink" href="#a-miscellany-of-leo-scripting" title="Permalink to this heading">¶</a></h1>
<p>This chapter covers miscellaneous topics related to Leo scripts.</p>
<p>You might call this a FAQ for scripts…</p>
<div class="contents local topic" id="contents">
<p class="topic-title">Contents</p>
<ul class="simple">
<li><p><a class="reference internal" href="#button-example" id="id1">&#64;button example</a></p></li>
<li><p><a class="reference internal" href="#comparing-two-similar-outlines" id="id2">Comparing two similar outlines</a></p></li>
<li><p><a class="reference internal" href="#converting-body-text-to-title-case" id="id3">Converting Body Text To Title Case</a></p></li>
<li><p><a class="reference internal" href="#creating-minimal-outlines" id="id4">Creating minimal outlines</a></p></li>
<li><p><a class="reference internal" href="#creating-qt-windows-from-leo-scripts" id="id5">Creating Qt Windows from Leo scripts</a></p></li>
<li><p><a class="reference internal" href="#cutting-and-pasting-text" id="id6">Cutting and pasting text</a></p></li>
<li><p><a class="reference internal" href="#g-app-gui-run-methods-run-dialogs" id="id7">g.app.gui.run* methods run dialogs</a></p></li>
<li><p><a class="reference internal" href="#getting-commander-preferences" id="id8">Getting commander preferences</a></p></li>
<li><p><a class="reference internal" href="#getting-configuration-settings" id="id9">Getting configuration settings</a></p></li>
<li><p><a class="reference internal" href="#getting-interactive-input-in-scripts-and-commands" id="id10">Getting interactive input in scripts and commands</a></p></li>
<li><p><a class="reference internal" href="#inserting-and-deleting-icons" id="id11">Inserting and deleting icons</a></p></li>
<li><p><a class="reference internal" href="#invoking-commands-from-scripts" id="id12">Invoking commands from scripts</a></p></li>
<li><p><a class="reference internal" href="#making-operations-undoable" id="id13">Making operations undoable</a></p></li>
<li><p><a class="reference internal" href="#modifying-plugins-with-script-scripts" id="id14">Modifying plugins with &#64;script scripts</a></p></li>
<li><p><a class="reference internal" href="#modifying-the-body-pane-directly" id="id15">Modifying the body pane directly</a></p></li>
<li><p><a class="reference internal" href="#recovering-vnodes" id="id16">Recovering vnodes</a></p></li>
<li><p><a class="reference internal" href="#recursive-import-script" id="id17">Recursive import script</a></p></li>
<li><p><a class="reference internal" href="#retaining-pointers-to-qt-windows" id="id18">Retaining pointers to Qt windows</a></p></li>
<li><p><a class="reference internal" href="#running-code-at-idle-time" id="id19">Running code at idle time</a></p></li>
<li><p><a class="reference internal" href="#running-code-in-separate-processes" id="id20">Running code in separate processes</a></p>
<ul>
<li><p><a class="reference internal" href="#using-subprocess-popen" id="id21">Using subprocess.popen</a></p>
<ul>
<li><p><a class="reference internal" href="#call-subprocess-popen-directly" id="id22">Call subprocess.popen directly</a></p></li>
<li><p><a class="reference internal" href="#call-g-execute-shell-commands" id="id23">Call g.execute_shell_commands</a></p></li>
<li><p><a class="reference internal" href="#call-g-execute-shell-commands-with-options" id="id24">Call g.execute_shell_commands_with_options</a></p></li>
</ul>
</li>
<li><p><a class="reference internal" href="#using-g-app-backgroundprocessmanager" id="id25">Using g.app.backgroundProcessManager</a></p></li>
<li><p><a class="reference internal" href="#using-g-execute-shell-commands" id="id26">Using g.execute_shell_commands</a></p></li>
</ul>
</li>
<li><p><a class="reference internal" href="#running-leo-in-batch-mode" id="id27">Running Leo in batch mode</a></p></li>
<li><p><a class="reference internal" href="#using-pyplot-and-matplotlib" id="id28">Using &#64;pyplot and matplotlib</a></p>
<ul>
<li><p><a class="reference internal" href="#overview" id="id29">Overview</a></p></li>
<li><p><a class="reference internal" href="#displaying-images-externally" id="id30">Displaying images externally</a></p></li>
</ul>
</li>
<li><p><a class="reference internal" href="#working-with-directives-and-paths" id="id31">Working with directives and paths</a></p></li>
<li><p><a class="reference internal" href="#writing-g-es-output-to-other-tabs" id="id32">Writing g.es output to other tabs</a></p></li>
<li><p><a class="reference internal" href="#writing-clickable-links-to-the-log-tab" id="id33">Writing clickable links to the Log tab</a></p></li>
</ul>
</div>
<section id="button-example">
<h2><a class="toc-backref" href="#id1">&#64;button example</a><a class="headerlink" href="#button-example" title="Permalink to this heading">¶</a></h2>
<p>The .leo files in Leo’s distribution contain many &#64;button nodes (many disabled), that do repetitive chores. Here is one, &#64;button promote-child-bodies, from LeoDocs.leo:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="sd">&#39;&#39;&#39;Copy the body text of all children to the parent&#39;s body text.&#39;&#39;&#39;</span>

<span class="c1"># Great for creating what&#39;s new nodes.</span>
<span class="n">result</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span><span class="o">.</span><span class="n">b</span><span class="p">]</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">undoer</span><span class="o">.</span><span class="n">beforeChangeNodeContents</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
<span class="k">for</span> <span class="n">child</span> <span class="ow">in</span> <span class="n">p</span><span class="o">.</span><span class="n">children</span><span class="p">():</span>
    <span class="k">if</span> <span class="n">child</span><span class="o">.</span><span class="n">b</span><span class="p">:</span>
        <span class="n">result</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">- </span><span class="si">%s</span><span class="se">\n\n</span><span class="si">%s</span><span class="se">\n</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">child</span><span class="o">.</span><span class="n">h</span><span class="p">,</span><span class="n">child</span><span class="o">.</span><span class="n">b</span><span class="p">))</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">result</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">- </span><span class="si">%s</span><span class="se">\n\n</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">child</span><span class="o">.</span><span class="n">h</span><span class="p">))</span>
<span class="n">p</span><span class="o">.</span><span class="n">b</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
<span class="n">c</span><span class="o">.</span><span class="n">undoer</span><span class="o">.</span><span class="n">afterChangeNodeContents</span><span class="p">(</span><span class="n">p</span><span class="p">,</span><span class="s1">&#39;promote-child-bodies&#39;</span><span class="p">,</span><span class="n">b</span><span class="p">)</span>
</pre></div>
</div>
<p>This creates a fully undoable promote-child-bodies command.</p>
</section>
<section id="comparing-two-similar-outlines">
<h2><a class="toc-backref" href="#id2">Comparing two similar outlines</a><a class="headerlink" href="#comparing-two-similar-outlines" title="Permalink to this heading">¶</a></h2>
<p>efc.compareTrees does most of the work of comparing two similar outlines.
For example, here is “&#64;button compare vr-controller” in leoPyRef.leo:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">p1</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">findNodeAnywhere</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="s1">&#39;class ViewRenderedController (QWidget) (vr)&#39;</span><span class="p">)</span>
<span class="n">p2</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">findNodeAnywhere</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="s1">&#39;class ViewRenderedController (QWidget) (vr2)&#39;</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">p1</span> <span class="ow">and</span> <span class="n">p2</span>
<span class="n">tag</span> <span class="o">=</span> <span class="s1">&#39;compare vr1 &amp; vr2&#39;</span>
<span class="n">c</span><span class="o">.</span><span class="n">editFileCommands</span><span class="o">.</span><span class="n">compareTrees</span><span class="p">(</span><span class="n">p1</span><span class="p">,</span> <span class="n">p2</span><span class="p">,</span> <span class="n">tag</span><span class="p">)</span>
</pre></div>
</div>
<p>This script will compare the trees whose roots are p1 and p2 and show the results like “Recovered nodes”.  That is, the script creates a node called “compare vr1 &amp; vr2”.  This top-level node contains one child node for every node that is different.  Each child node contains a diff of the node.  The grand children are one or two clones of the changed or inserted node.</p>
</section>
<section id="converting-body-text-to-title-case">
<h2><a class="toc-backref" href="#id3">Converting Body Text To Title Case</a><a class="headerlink" href="#converting-body-text-to-title-case" title="Permalink to this heading">¶</a></h2>
<p>Title case means that all words start with capital letters.  The
following script converts the selected body text to title case.  If
nothing has been selected, the entire current node is converted. The
conversion is undoable:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="sd">&quot;&quot;&quot;Undoably convert selection or body to title case.&quot;&quot;&quot;</span>
<span class="n">w</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">wrapper</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">p</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">b</span>
<span class="n">u</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">undoer</span>

<span class="n">start</span><span class="p">,</span> <span class="n">end</span> <span class="o">=</span> <span class="n">w</span><span class="o">.</span><span class="n">getSelectionRange</span><span class="p">()</span>
<span class="n">use_entire</span> <span class="o">=</span> <span class="n">start</span> <span class="o">==</span> <span class="n">end</span>  <span class="c1"># no selection, convert entire body</span>

<span class="n">undoType</span> <span class="o">=</span> <span class="s1">&#39;title-case-body-selection&#39;</span>
<span class="n">undoData</span> <span class="o">=</span> <span class="n">u</span><span class="o">.</span><span class="n">beforeChangeNodeContents</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>

<span class="k">if</span> <span class="n">use_entire</span><span class="p">:</span>
    <span class="n">p</span><span class="o">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">title</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
    <span class="n">sel</span> <span class="o">=</span> <span class="n">s</span><span class="p">[</span><span class="n">start</span><span class="p">:</span><span class="n">end</span><span class="p">]</span>
    <span class="n">head</span><span class="p">,</span> <span class="n">tail</span> <span class="o">=</span> <span class="n">s</span><span class="p">[:</span><span class="n">start</span><span class="p">],</span> <span class="n">s</span><span class="p">[</span><span class="n">end</span><span class="p">:]</span>
    <span class="n">p</span><span class="o">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">head</span> <span class="o">+</span> <span class="n">sel</span><span class="o">.</span><span class="n">title</span><span class="p">()</span> <span class="o">+</span> <span class="n">tail</span>

<span class="n">c</span><span class="o">.</span><span class="n">setChanged</span><span class="p">()</span>
<span class="n">p</span><span class="o">.</span><span class="n">setDirty</span><span class="p">()</span>
<span class="n">u</span><span class="o">.</span><span class="n">afterChangeNodeContents</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">undoType</span><span class="p">,</span> <span class="n">undoData</span><span class="p">)</span>
<span class="n">c</span><span class="o">.</span><span class="n">redraw</span><span class="p">()</span>
</pre></div>
</div>
<p>[Contributed by T. B. Passin]</p>
</section>
<section id="creating-minimal-outlines">
<h2><a class="toc-backref" href="#id4">Creating minimal outlines</a><a class="headerlink" href="#creating-minimal-outlines" title="Permalink to this heading">¶</a></h2>
<p>The following script will create a minimal Leo outline:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="mi">1</span><span class="p">:</span>
    <span class="c1"># Create a visible frame.</span>
    <span class="n">c2</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">newCommander</span><span class="p">(</span><span class="n">fileName</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
    <span class="c1"># Create an invisible frame.</span>
    <span class="n">c2</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">newCommander</span><span class="p">(</span><span class="n">fileName</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span><span class="n">gui</span><span class="o">=</span><span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">nullGui</span><span class="p">)</span>

<span class="n">c2</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">createFirstTreeNode</span><span class="p">()</span>
<span class="n">c2</span><span class="o">.</span><span class="n">redraw</span><span class="p">()</span>

<span class="c1"># Test that the script works.</span>
<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">c2</span><span class="o">.</span><span class="n">all_positions</span><span class="p">():</span>
    <span class="n">g</span><span class="o">.</span><span class="n">es</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">h</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="creating-qt-windows-from-leo-scripts">
<h2><a class="toc-backref" href="#id5">Creating Qt Windows from Leo scripts</a><a class="headerlink" href="#creating-qt-windows-from-leo-scripts" title="Permalink to this heading">¶</a></h2>
<p>The following puts up a test window when run as a Leo script:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">PyQt5</span> <span class="kn">import</span> <span class="n">QtGui</span>
<span class="n">w</span> <span class="o">=</span> <span class="n">QtGui</span><span class="o">.</span><span class="n">QWidget</span><span class="p">()</span>
<span class="n">w</span><span class="o">.</span><span class="n">resize</span><span class="p">(</span><span class="mi">250</span><span class="p">,</span> <span class="mi">150</span><span class="p">)</span>
<span class="n">w</span><span class="o">.</span><span class="n">move</span><span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">300</span><span class="p">)</span>
<span class="n">w</span><span class="o">.</span><span class="n">setWindowTitle</span><span class="p">(</span><span class="s1">&#39;Simple test&#39;</span><span class="p">)</span>
<span class="n">w</span><span class="o">.</span><span class="n">show</span><span class="p">()</span>
<span class="n">c</span><span class="o">.</span><span class="n">my_test</span> <span class="o">=</span> <span class="n">w</span> <span class="c1"># &lt;-- Keep a reference to the window!</span>
</pre></div>
</div>
<p><strong>Important</strong>: Something like the last line is essential. Without it, the window would immediately disappear after being created.  The assignment:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">c</span><span class="o">.</span><span class="n">my_test</span> <span class="o">=</span> <span class="n">w</span>
</pre></div>
</div>
<p>creates a permanent reference to the window so the window won’t be garbage collected after the Leo script exits.</p>
</section>
<section id="cutting-and-pasting-text">
<h2><a class="toc-backref" href="#id6">Cutting and pasting text</a><a class="headerlink" href="#cutting-and-pasting-text" title="Permalink to this heading">¶</a></h2>
<p>The following shows how to cut and paste text to the clipboard:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">gui</span><span class="o">.</span><span class="n">replaceClipboardWith</span><span class="p">(</span><span class="s1">&#39;hi&#39;</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">gui</span><span class="o">.</span><span class="n">getTextFromClipboard</span><span class="p">())</span>
</pre></div>
</div>
</section>
<section id="g-app-gui-run-methods-run-dialogs">
<h2><a class="toc-backref" href="#id7">g.app.gui.run* methods run dialogs</a><a class="headerlink" href="#g-app-gui-run-methods-run-dialogs" title="Permalink to this heading">¶</a></h2>
<p>Scripts can invoke various dialogs using the following methods of the g.app.gui object.</p>
<p>Here is a partial list. Use typing completion to get the full list:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">gui</span><span class="o">.</span><span class="n">runAskOkCancelNumberDialog</span><span class="p">(</span><span class="n">c</span><span class="p">,</span><span class="n">title</span><span class="p">,</span><span class="n">message</span><span class="p">)</span>
<span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">gui</span><span class="o">.</span><span class="n">runAskOkCancelStringDialog</span><span class="p">(</span><span class="n">c</span><span class="p">,</span><span class="n">title</span><span class="p">,</span><span class="n">message</span><span class="p">)</span>
<span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">gui</span><span class="o">.</span><span class="n">runAskOkDialog</span><span class="p">(</span><span class="n">c</span><span class="p">,</span><span class="n">title</span><span class="p">,</span><span class="n">message</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span><span class="n">text</span><span class="o">=</span><span class="s1">&#39;Ok&#39;</span><span class="p">)</span>
<span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">gui</span><span class="o">.</span><span class="n">runAskYesNoCancelDialog</span><span class="p">(</span><span class="n">c</span><span class="p">,</span><span class="n">title</span><span class="p">,</span><span class="n">message</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
    <span class="n">yesMessage</span><span class="o">=</span><span class="s1">&#39;Yes&#39;</span><span class="p">,</span><span class="n">noMessage</span><span class="o">=</span><span class="s1">&#39;No&#39;</span><span class="p">,</span><span class="n">defaultButton</span><span class="o">=</span><span class="s1">&#39;Yes&#39;</span><span class="p">)</span>
<span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">gui</span><span class="o">.</span><span class="n">runAskYesNoDialog</span><span class="p">(</span><span class="n">c</span><span class="p">,</span><span class="n">title</span><span class="p">,</span><span class="n">message</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>
</pre></div>
</div>
<p>The values returned are in (‘ok’,’yes’,’no’,’cancel’), as indicated by the method names. Some dialogs also return strings or numbers, again as indicated by their names.</p>
<p>Scripts can run File Open and Save dialogs with these methods:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">gui</span><span class="o">.</span><span class="n">runOpenFileDialog</span><span class="p">(</span><span class="n">title</span><span class="p">,</span><span class="n">filetypes</span><span class="p">,</span><span class="n">defaultextension</span><span class="p">,</span><span class="n">multiple</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">gui</span><span class="o">.</span><span class="n">runSaveFileDialog</span><span class="p">(</span><span class="n">initialfile</span><span class="p">,</span><span class="n">title</span><span class="p">,</span><span class="n">filetypes</span><span class="p">,</span><span class="n">defaultextension</span><span class="p">)</span>
</pre></div>
</div>
<p>For details about how to use these file dialogs, look for examples in Leo’s own source code. The runOpenFileDialog returns a list of file names.</p>
</section>
<section id="getting-commander-preferences">
<h2><a class="toc-backref" href="#id8">Getting commander preferences</a><a class="headerlink" href="#getting-commander-preferences" title="Permalink to this heading">¶</a></h2>
<p>Each commander sets ivars corresponding to settings.</p>
<p>Scripts can get the following ivars of the Commands class:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">ivars</span> <span class="o">=</span> <span class="p">(</span>
    <span class="s1">&#39;output_doc_flag&#39;</span><span class="p">,</span>
    <span class="s1">&#39;page_width&#39;</span><span class="p">,</span>
    <span class="s1">&#39;page_width&#39;</span><span class="p">,</span>
    <span class="s1">&#39;tab_width&#39;</span><span class="p">,</span>
    <span class="s1">&#39;target_language&#39;</span><span class="p">,</span>
    <span class="s1">&#39;use_header_flag&#39;</span><span class="p">,</span>
<span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Prefs ivars...</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span><span class="n">color</span><span class="o">=</span><span class="s2">&quot;purple&quot;</span><span class="p">)</span>
<span class="k">for</span> <span class="n">ivar</span> <span class="ow">in</span> <span class="n">ivars</span><span class="p">:</span>
    <span class="nb">print</span><span class="p">(</span><span class="nb">getattr</span><span class="p">(</span><span class="n">c</span><span class="p">,</span><span class="n">ivar</span><span class="p">))</span>
</pre></div>
</div>
<p>If your script sets c.tab_width it should call f.setTabWidth to redraw the screen:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">c</span><span class="o">.</span><span class="n">tab_width</span> <span class="o">=</span> <span class="o">-</span><span class="mi">4</span>    <span class="c1"># Change this and see what happens.</span>
<span class="n">c</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">setTabWidth</span><span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">tab_width</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="getting-configuration-settings">
<h2><a class="toc-backref" href="#id9">Getting configuration settings</a><a class="headerlink" href="#getting-configuration-settings" title="Permalink to this heading">¶</a></h2>
<p>Settings may be different for each commander.</p>
<p>The c.config class has the following getters.</p>
<ul class="simple">
<li><p>c.config.getBool(settingName,default=None)</p></li>
<li><p>c.config.getColor(settingName)</p></li>
<li><p>c.config.getDirectory(settingName)</p></li>
<li><p>c.config.getFloat(settingName)</p></li>
<li><p>c.config.getInt(settingName)</p></li>
<li><p>c.config.getLanguage(settingName)</p></li>
<li><p>c.config.getRatio(settingName)</p></li>
<li><p>c.config.getShortcut(settingName)</p></li>
<li><p>c.config.getString(settingName)</p></li>
</ul>
<p>These methods return None if no setting exists.</p>
<p>The getBool ‘default’ argument to getBool specifies the value to be returned if the setting does not exist.</p>
</section>
<section id="getting-interactive-input-in-scripts-and-commands">
<h2><a class="toc-backref" href="#id10">Getting interactive input in scripts and commands</a><a class="headerlink" href="#getting-interactive-input-in-scripts-and-commands" title="Permalink to this heading">¶</a></h2>
<p>k.get1Arg handles the next character the user types when accumulating a user argument from the minibuffer.  k.get1Arg handles details such as tab completion, backspacing, Ctrl-G etc.</p>
<p>Commands should use k.get1Arg to get the first minibuffer argument and k.getNextArg to get all other arguments.</p>
<p>k.get1Arg is a state machine. It has to be because it’s almost always waiting for the user to type the next character. The handle keyword arg specifies the next state in the machine.</p>
<p>The following examples will work in any class having a ‘c’ ivar bound to a commander.</p>
<p>Example 1: get one argument from the user:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@cmd</span><span class="p">(</span><span class="s1">&#39;my-command&#39;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">myCommand</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">event</span><span class="p">):</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;State 0&quot;&quot;&quot;</span>
    <span class="n">k</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">c</span><span class="o">.</span><span class="n">k</span>
    <span class="n">k</span><span class="o">.</span><span class="n">setLabelBlue</span><span class="p">(</span><span class="s1">&#39;prompt: &#39;</span><span class="p">)</span>
    <span class="n">k</span><span class="o">.</span><span class="n">get1Arg</span><span class="p">(</span><span class="n">event</span><span class="p">,</span> <span class="n">handler</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">myCommand1</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">myCommand1</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">event</span><span class="p">):</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;State 1&quot;&quot;&quot;</span>
    <span class="n">k</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">c</span><span class="o">.</span><span class="n">k</span>
    <span class="c1"># ----&gt; k.arg contains the argument.</span>
    <span class="c1"># Finish the command.</span>
    <span class="o">...</span>
    <span class="c1"># Reset the minibuffer.</span>
    <span class="n">k</span><span class="o">.</span><span class="n">clearState</span><span class="p">()</span>
    <span class="n">k</span><span class="o">.</span><span class="n">resetLabel</span><span class="p">()</span>
    <span class="n">k</span><span class="o">.</span><span class="n">showStateAndMode</span><span class="p">()</span>
</pre></div>
</div>
<p>Example 2: get two arguments from the user:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@cmd</span><span class="p">(</span><span class="s1">&#39;my-command&#39;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">myCommand</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">event</span><span class="p">):</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;State 0&quot;&quot;&quot;</span>
    <span class="n">k</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">c</span><span class="o">.</span><span class="n">k</span>
    <span class="n">k</span><span class="o">.</span><span class="n">setLabelBlue</span><span class="p">(</span><span class="s1">&#39;first prompt: &#39;</span><span class="p">)</span>
    <span class="n">k</span><span class="o">.</span><span class="n">get1Arg</span><span class="p">(</span><span class="n">event</span><span class="p">,</span> <span class="n">handler</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">myCommand1</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">myCommand1</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">event</span><span class="p">):</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;State 1&quot;&quot;&quot;</span>
    <span class="n">k</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">c</span><span class="o">.</span><span class="n">k</span>
    <span class="bp">self</span><span class="o">.</span><span class="n">arg1</span> <span class="o">=</span> <span class="n">k</span><span class="o">.</span><span class="n">arg</span>
    <span class="n">k</span><span class="o">.</span><span class="n">setLabelBlue</span><span class="p">(</span><span class="s1">&#39;second prompt: &#39;</span><span class="p">)</span>
    <span class="n">k</span><span class="o">.</span><span class="n">getNextArg</span><span class="p">(</span><span class="n">handler</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">myCommand2</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">myCommand2</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">event</span><span class="p">):</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;State 2&quot;&quot;&quot;</span>
    <span class="n">k</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">c</span><span class="o">.</span><span class="n">k</span>
    <span class="c1"># -----&gt; k.arg contains second argument.</span>
    <span class="c1"># Finish the command, using self.arg1 and k.arg.</span>
    <span class="o">...</span>
    <span class="c1"># Reset the minibuffer.</span>
    <span class="n">k</span><span class="o">.</span><span class="n">clearState</span><span class="p">()</span>
    <span class="n">k</span><span class="o">.</span><span class="n">resetLabel</span><span class="p">()</span>
    <span class="n">k</span><span class="o">.</span><span class="n">showStateAndMode</span><span class="p">()</span>
</pre></div>
</div>
<p><strong>Summary</strong></p>
<ul class="simple">
<li><p>The handler keyword argument to k.get1Arg and k.getNextArg specifies the next state in the state machine.</p></li>
<li><p>k.get1Arg contains many optional keyword arguments. See its docstring for details.</p></li>
</ul>
</section>
<section id="inserting-and-deleting-icons">
<h2><a class="toc-backref" href="#id11">Inserting and deleting icons</a><a class="headerlink" href="#inserting-and-deleting-icons" title="Permalink to this heading">¶</a></h2>
<p>You can add an icon to the presently selected node with c.editCommands.insertIconFromFile(path). path is an absolute path or a path relative to the leo/Icons folder. A relative path is recommended if you plan to use the icons on machines with different directory structures.</p>
<p>For example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">path</span> <span class="o">=</span> <span class="s1">&#39;rt_arrow_disabled.gif&#39;</span>
<span class="n">c</span><span class="o">.</span><span class="n">editCommands</span><span class="o">.</span><span class="n">insertIconFromFile</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
</pre></div>
</div>
<p>Scripts can delete icons from the presently selected node using the following methods:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">c</span><span class="o">.</span><span class="n">editCommands</span><span class="o">.</span><span class="n">deleteFirstIcon</span><span class="p">()</span>
<span class="n">c</span><span class="o">.</span><span class="n">editCommands</span><span class="o">.</span><span class="n">deleteLastIcon</span><span class="p">()</span>
<span class="n">c</span><span class="o">.</span><span class="n">editCommands</span><span class="o">.</span><span class="n">deleteNodeIcons</span><span class="p">()</span>
</pre></div>
</div>
</section>
<section id="invoking-commands-from-scripts">
<h2><a class="toc-backref" href="#id12">Invoking commands from scripts</a><a class="headerlink" href="#invoking-commands-from-scripts" title="Permalink to this heading">¶</a></h2>
<p>You can invoke minibuffer commands by name.  For example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">c</span><span class="o">.</span><span class="n">executeMinibufferCommand</span><span class="p">(</span><span class="s1">&#39;open-outline&#39;</span><span class="p">)</span>
</pre></div>
</div>
<p>c.keyHandler.funcReturn contains the value returned from the command. In many cases, as above, this value is simply ‘break’.</p>
</section>
<section id="making-operations-undoable">
<h2><a class="toc-backref" href="#id13">Making operations undoable</a><a class="headerlink" href="#making-operations-undoable" title="Permalink to this heading">¶</a></h2>
<p>Plugins and scripts should call u.beforeX and u.afterX methods to describe the operation that is being performed. <strong>Note</strong>: u is shorthand for c.undoer. Most u.beforeX methods return undoData that the client code merely passes to the corresponding u.afterX method. This data contains the ‘before’ snapshot. The u.afterX methods then create a bead containing both the ‘before’ and ‘after’ snapshots.</p>
<p>u.beforeChangeGroup and u.afterChangeGroup allow multiple calls to u.beforeX and u.afterX methods to be treated as a single undoable entry. See the code for the Replace All, Sort, Promote and Demote commands for examples. The u.beforeChangeGroup and u.afterChangeGroup methods substantially reduce the number of u.beforeX and afterX methods needed.</p>
<p>Plugins and scripts may define their own u.beforeX and afterX methods. Indeed, u.afterX merely needs to set the bunch.undoHelper and bunch.redoHelper ivars to the methods used to undo and redo the operation. See the code for the various u.beforeX and afterX methods for guidance.</p>
<p>See the section &lt;&lt; How Leo implements unlimited undo &gt;&gt; in leoUndo.py for more details. In general, the best way to see how to implement undo is to see how Leo’s core calls the u.beforeX and afterX methods.</p>
</section>
<section id="modifying-plugins-with-script-scripts">
<h2><a class="toc-backref" href="#id14">Modifying plugins with &#64;script scripts</a><a class="headerlink" href="#modifying-plugins-with-script-scripts" title="Permalink to this heading">¶</a></h2>
<p>The mod_scripting plugin runs &#64;scripts before plugin initiation is complete. Thus, such scripts can not directly modify plugins. Instead, a script can create an event handler for the after-create-leo-frame that will modify the plugin.</p>
<p>For example, the following modifies the cleo.py plugin after Leo has completed loading it:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">prikey</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">v</span><span class="p">):</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">pa</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">getat</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="s1">&#39;priority&#39;</span><span class="p">))</span>
    <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
        <span class="n">pa</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span>

    <span class="k">if</span> <span class="n">pa</span> <span class="o">==</span> <span class="mi">24</span><span class="p">:</span>
        <span class="n">pa</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span>
    <span class="k">if</span> <span class="n">pa</span> <span class="o">==</span> <span class="mi">10</span><span class="p">:</span>
        <span class="n">pa</span> <span class="o">=</span> <span class="o">-</span><span class="mi">2</span>

    <span class="k">return</span> <span class="n">pa</span>

<span class="kn">import</span> <span class="nn">types</span>
<span class="kn">from</span> <span class="nn">leo.core</span> <span class="kn">import</span> <span class="n">leoPlugins</span>

<span class="k">def</span> <span class="nf">on_create</span><span class="p">(</span><span class="n">tag</span><span class="p">,</span> <span class="n">keywords</span><span class="p">):</span>
    <span class="n">c</span><span class="o">.</span><span class="n">cleo</span><span class="o">.</span><span class="n">prikey</span> <span class="o">=</span> <span class="n">types</span><span class="o">.</span><span class="n">MethodType</span><span class="p">(</span><span class="n">prikey</span><span class="p">,</span> <span class="n">c</span><span class="o">.</span><span class="n">cleo</span><span class="p">,</span> <span class="n">c</span><span class="o">.</span><span class="n">cleo</span><span class="o">.</span><span class="vm">__class__</span><span class="p">)</span>

<span class="n">leoPlugins</span><span class="o">.</span><span class="n">registerHandler</span><span class="p">(</span><span class="s2">&quot;after-create-leo-frame&quot;</span><span class="p">,</span><span class="n">on_create</span><span class="p">)</span>
</pre></div>
</div>
<p>Attempting to modify c.cleo.prikey immediately in the &#64;script gives an AttributeError as c has no .cleo when the &#64;script is executed. Deferring it by using registerHandler() avoids the problem.</p>
</section>
<section id="modifying-the-body-pane-directly">
<h2><a class="toc-backref" href="#id15">Modifying the body pane directly</a><a class="headerlink" href="#modifying-the-body-pane-directly" title="Permalink to this heading">¶</a></h2>
<p><strong>Important</strong>: The changes you make below <strong>will not persist</strong> unless your script calls c.frame.body.onBodyChanged after making those changes. This method has the following signature:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">onBodyChanged</span> <span class="p">(</span><span class="bp">self</span><span class="p">,</span><span class="n">undoType</span><span class="p">,</span><span class="n">oldSel</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span><span class="n">oldText</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span><span class="n">oldYview</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
</pre></div>
</div>
<p>Let:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">w</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">wrapper</span> <span class="c1"># Leo&#39;s body pane.</span>
</pre></div>
</div>
<p>Scripts can get or change the context of the body as follows:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">w</span><span class="o">.</span><span class="n">appendText</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>                     <span class="c1"># Append s to end of body text.</span>
<span class="n">w</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>                  <span class="c1"># Delete characters from i to j.</span>
<span class="n">w</span><span class="o">.</span><span class="n">deleteTextSelection</span><span class="p">()</span>             <span class="c1"># Delete the selected text, if any.</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">w</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>                 <span class="c1"># Return the text from i to j.</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">w</span><span class="o">.</span><span class="n">getAllText</span>                    <span class="c1"># Return the entire body text.</span>
<span class="n">i</span> <span class="o">=</span> <span class="n">w</span><span class="o">.</span><span class="n">getInsertPoint</span><span class="p">()</span>              <span class="c1"># Return the location of the cursor.</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">w</span><span class="o">.</span><span class="n">getSelectedText</span><span class="p">()</span>             <span class="c1"># Return the selected text, if any.</span>
<span class="n">i</span><span class="p">,</span><span class="n">j</span> <span class="o">=</span> <span class="n">w</span><span class="o">.</span><span class="n">getSelectionRange</span> <span class="p">(</span><span class="n">sort</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="c1"># Return the range of selected text.</span>
<span class="n">w</span><span class="o">.</span><span class="n">setAllText</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>                     <span class="c1"># Set the entire body text to s.</span>
<span class="n">w</span><span class="o">.</span><span class="n">setSelectionRange</span><span class="p">(</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">,</span><span class="n">insert</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span> <span class="c1"># Select the text.</span>
</pre></div>
</div>
<p><strong>Notes</strong>:</p>
<ul class="simple">
<li><p>These are only the most commonly-used methods. For more information, consult Leo’s source code.</p></li>
<li><p>i and j are zero-based indices into the the text. When j is not specified, it defaults to i. When the sort parameter is in effect, getSelectionRange ensures i &lt;= j.</p></li>
<li><p>color is a Tk color name, even when using the Gt gui.</p></li>
</ul>
</section>
<section id="recovering-vnodes">
<h2><a class="toc-backref" href="#id16">Recovering vnodes</a><a class="headerlink" href="#recovering-vnodes" title="Permalink to this heading">¶</a></h2>
<p>Positions become invalid whenever the outline changes.</p>
<p>This script finds a position p2 having the same vnode as an invalid position p:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="ow">not</span> <span class="n">c</span><span class="o">.</span><span class="n">positionExists</span><span class="p">(</span><span class="n">p</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">p2</span> <span class="ow">in</span> <span class="n">c</span><span class="o">.</span><span class="n">all_positions</span><span class="p">():</span>
        <span class="k">if</span> <span class="n">p2</span><span class="o">.</span><span class="n">v</span> <span class="o">==</span> <span class="n">p</span><span class="o">.</span><span class="n">v</span><span class="p">:</span> <span class="c1"># found</span>
            <span class="n">c</span><span class="o">.</span><span class="n">selectPosition</span><span class="p">(</span><span class="n">p2</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;position no longer exists&#39;</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="recursive-import-script">
<h2><a class="toc-backref" href="#id17">Recursive import script</a><a class="headerlink" href="#recursive-import-script" title="Permalink to this heading">¶</a></h2>
<p>The following script imports files from a given directory and all subdirectories:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">c</span><span class="o">.</span><span class="n">recursiveImport</span><span class="p">(</span>
    <span class="n">dir_</span> <span class="o">=</span> <span class="s1">&#39;path to file or directory&#39;</span><span class="p">,</span>
    <span class="n">kind</span> <span class="o">=</span> <span class="s1">&#39;@clean&#39;</span><span class="p">,</span>        <span class="c1"># or &#39;@file&#39; or &#39;@auto&#39;</span>
    <span class="n">one_file</span> <span class="o">=</span> <span class="kc">False</span><span class="p">,</span>       <span class="c1"># True: import only one file.</span>
    <span class="n">safe_at_file</span> <span class="o">=</span> <span class="kc">False</span><span class="p">,</span>   <span class="c1"># True: generate @@clean nodes.</span>
    <span class="n">theTypes</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>        <span class="c1"># Same as [&#39;.py&#39;]</span>
<span class="p">)</span>
</pre></div>
</div>
</section>
<section id="retaining-pointers-to-qt-windows">
<h2><a class="toc-backref" href="#id18">Retaining pointers to Qt windows</a><a class="headerlink" href="#retaining-pointers-to-qt-windows" title="Permalink to this heading">¶</a></h2>
<p>&#64;pagewidth 75</p>
<p>The following script won’t work as intended:</p>
<blockquote>
<div><p>from PyQt5 import QtGui
w = QtGui.QWidget()
w.resize(250, 150)
w.move(300, 300)
w.setWindowTitle(‘Simple test’)
w.show()</p>
</div></blockquote>
<p>When the script exits the sole reference to the window, w, ceases to exist, so the window is destroyed (garbage collected). To keep the window open, add the following code as the last line to keep the reference alive:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">scriptsDict</span><span class="p">[</span><span class="s1">&#39;my-script_w&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">w</span>
</pre></div>
</div>
<p>Note that this reference will persist until the next time you run the execute-script. If you want something even more permanent, you can do something like:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">my_script_w</span> <span class="o">=</span> <span class="n">w</span>
</pre></div>
</div>
</section>
<section id="running-code-at-idle-time">
<h2><a class="toc-backref" href="#id19">Running code at idle time</a><a class="headerlink" href="#running-code-at-idle-time" title="Permalink to this heading">¶</a></h2>
<p>Scripts and plugins can call g.app.idleTimeManager.add_callback(callback) to cause
the callback to be called at idle time forever. This should suffice for most purposes:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">print_hi</span><span class="p">():</span>
    <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;hi&#39;</span><span class="p">)</span>

<span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">idleTimeManager</span><span class="o">.</span><span class="n">add_callback</span><span class="p">(</span><span class="n">print_hi</span><span class="p">)</span>
</pre></div>
</div>
<p>For greater control, g.IdleTime is a thin wrapper for the Leo’s IdleTime class. The IdleTime class executes a handler with a given delay at idle time. The handler takes a single argument, the IdleTime instance:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">handler</span><span class="p">(</span><span class="n">it</span><span class="p">):</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;IdleTime handler.  it is an IdleTime instance.&quot;&quot;&quot;</span>
    <span class="n">delta_t</span> <span class="o">=</span> <span class="n">it</span><span class="o">.</span><span class="n">time</span><span class="o">-</span><span class="n">it</span><span class="o">.</span><span class="n">starting_time</span>
    <span class="n">g</span><span class="o">.</span><span class="n">trace</span><span class="p">(</span><span class="n">it</span><span class="o">.</span><span class="n">count</span><span class="p">,</span><span class="n">it</span><span class="o">.</span><span class="n">c</span><span class="o">.</span><span class="n">shortFileName</span><span class="p">(),</span><span class="s1">&#39;</span><span class="si">%2.4f</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">delta_t</span><span class="p">))</span>
    <span class="k">if</span> <span class="n">it</span><span class="o">.</span><span class="n">count</span> <span class="o">&gt;=</span> <span class="mi">5</span><span class="p">:</span>
        <span class="n">g</span><span class="o">.</span><span class="n">trace</span><span class="p">(</span><span class="s1">&#39;done&#39;</span><span class="p">)</span>
        <span class="n">it</span><span class="o">.</span><span class="n">stop</span><span class="p">()</span>

<span class="c1"># Execute handler every 500 msec. at idle time.</span>
<span class="n">it</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">IdleTime</span><span class="p">(</span><span class="n">handler</span><span class="p">,</span><span class="n">delay</span><span class="o">=</span><span class="mi">500</span><span class="p">)</span>
<span class="k">if</span> <span class="n">it</span><span class="p">:</span> <span class="n">it</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</pre></div>
</div>
<p>The code creates an instance of the IdleTime class that calls the given handler at idle time, and no more than once every 500 msec.  Here is the output:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">handler</span> <span class="mi">1</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">0.5100</span>
<span class="n">handler</span> <span class="mi">2</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">1.0300</span>
<span class="n">handler</span> <span class="mi">3</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">1.5400</span>
<span class="n">handler</span> <span class="mi">4</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">2.0500</span>
<span class="n">handler</span> <span class="mi">5</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">2.5610</span>
<span class="n">handler</span> <span class="n">done</span>
</pre></div>
</div>
<p>Timer instances are completely independent:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">handler1</span><span class="p">(</span><span class="n">it</span><span class="p">):</span>
    <span class="n">delta_t</span> <span class="o">=</span> <span class="n">it</span><span class="o">.</span><span class="n">time</span><span class="o">-</span><span class="n">it</span><span class="o">.</span><span class="n">starting_time</span>
    <span class="n">g</span><span class="o">.</span><span class="n">trace</span><span class="p">(</span><span class="s1">&#39;</span><span class="si">%2s</span><span class="s1"> </span><span class="si">%s</span><span class="s1"> </span><span class="si">%2.4f</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">it</span><span class="o">.</span><span class="n">count</span><span class="p">,</span><span class="n">it</span><span class="o">.</span><span class="n">c</span><span class="o">.</span><span class="n">shortFileName</span><span class="p">(),</span><span class="n">delta_t</span><span class="p">))</span>
    <span class="k">if</span> <span class="n">it</span><span class="o">.</span><span class="n">count</span> <span class="o">&gt;=</span> <span class="mi">5</span><span class="p">:</span>
        <span class="n">g</span><span class="o">.</span><span class="n">trace</span><span class="p">(</span><span class="s1">&#39;done&#39;</span><span class="p">)</span>
        <span class="n">it</span><span class="o">.</span><span class="n">stop</span><span class="p">()</span>

<span class="k">def</span> <span class="nf">handler2</span><span class="p">(</span><span class="n">it</span><span class="p">):</span>
    <span class="n">delta_t</span> <span class="o">=</span> <span class="n">it</span><span class="o">.</span><span class="n">time</span><span class="o">-</span><span class="n">it</span><span class="o">.</span><span class="n">starting_time</span>
    <span class="n">g</span><span class="o">.</span><span class="n">trace</span><span class="p">(</span><span class="s1">&#39;</span><span class="si">%2s</span><span class="s1"> </span><span class="si">%s</span><span class="s1"> </span><span class="si">%2.4f</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">it</span><span class="o">.</span><span class="n">count</span><span class="p">,</span><span class="n">it</span><span class="o">.</span><span class="n">c</span><span class="o">.</span><span class="n">shortFileName</span><span class="p">(),</span><span class="n">delta_t</span><span class="p">))</span>
    <span class="k">if</span> <span class="n">it</span><span class="o">.</span><span class="n">count</span> <span class="o">&gt;=</span> <span class="mi">10</span><span class="p">:</span>
        <span class="n">g</span><span class="o">.</span><span class="n">trace</span><span class="p">(</span><span class="s1">&#39;done&#39;</span><span class="p">)</span>
        <span class="n">it</span><span class="o">.</span><span class="n">stop</span><span class="p">()</span>

<span class="n">it1</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">IdleTime</span><span class="p">(</span><span class="n">handler1</span><span class="p">,</span><span class="n">delay</span><span class="o">=</span><span class="mi">500</span><span class="p">)</span>
<span class="n">it2</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">IdleTime</span><span class="p">(</span><span class="n">handler2</span><span class="p">,</span><span class="n">delay</span><span class="o">=</span><span class="mi">1000</span><span class="p">)</span>
<span class="k">if</span> <span class="n">it1</span> <span class="ow">and</span> <span class="n">it2</span><span class="p">:</span>
    <span class="n">it1</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
    <span class="n">it2</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</pre></div>
</div>
<p>Here is the output:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">handler1</span>  <span class="mi">1</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">0.5200</span>
<span class="n">handler2</span>  <span class="mi">1</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">1.0100</span>
<span class="n">handler1</span>  <span class="mi">2</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">1.0300</span>
<span class="n">handler1</span>  <span class="mi">3</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">1.5400</span>
<span class="n">handler2</span>  <span class="mi">2</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">2.0300</span>
<span class="n">handler1</span>  <span class="mi">4</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">2.0600</span>
<span class="n">handler1</span>  <span class="mi">5</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">2.5600</span>
<span class="n">handler1</span> <span class="n">done</span>
<span class="n">handler2</span>  <span class="mi">3</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">3.0400</span>
<span class="n">handler2</span>  <span class="mi">4</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">4.0600</span>
<span class="n">handler2</span>  <span class="mi">5</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">5.0700</span>
<span class="n">handler2</span>  <span class="mi">6</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">6.0800</span>
<span class="n">handler2</span>  <span class="mi">7</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">7.1000</span>
<span class="n">handler2</span>  <span class="mi">8</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">8.1100</span>
<span class="n">handler2</span>  <span class="mi">9</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">9.1300</span>
<span class="n">handler2</span> <span class="mi">10</span> <span class="n">ekr</span><span class="o">.</span><span class="n">leo</span> <span class="mf">10.1400</span>
<span class="n">handler2</span> <span class="n">done</span>
</pre></div>
</div>
<p><strong>Recycling timers</strong></p>
<p>The g.app.idle_timers list retrains references to all timers so they <em>won’t</em> be recycled after being stopped.  This allows timers to be restarted safely.</p>
<p>There is seldom a need to recycle a timer, but if you must, you can call its destroySelf method. This removes the reference to the timer in g.app.idle_timers. <strong>Warning</strong>: Accessing a timer after calling its destroySelf method can lead to a hard crash.</p>
</section>
<section id="running-code-in-separate-processes">
<h2><a class="toc-backref" href="#id20">Running code in separate processes</a><a class="headerlink" href="#running-code-in-separate-processes" title="Permalink to this heading">¶</a></h2>
<p>It is dead easy for scripts, including &#64;button scripts, plugins, etc., to drive any external processes, including compilers and interpreters, from within Leo.</p>
<ul class="simple">
<li><p>The first section discusses three ways of calling subprocess.popen directly or via Leo helper functions.</p></li>
<li><p>The second section discusses the BackgroundProcessManager class.  Leo’s pylint command uses this class to run pylint commands sequentially <em>without blocking Leo</em>. Running processes sequentially prevents unwanted interleaving of output.</p></li>
<li><p>The last two sections discuss using g.execute_shell_commands and g.execute_shell_commands_with_options.</p></li>
</ul>
<section id="using-subprocess-popen">
<h3><a class="toc-backref" href="#id21">Using subprocess.popen</a><a class="headerlink" href="#using-subprocess-popen" title="Permalink to this heading">¶</a></h3>
<p>The first section discusses three <em>easy</em> ways to run code in a separate process by calling subprocess.popen either directly or via Leo helper functions.</p>
<section id="call-subprocess-popen-directly">
<h4><a class="toc-backref" href="#id22">Call subprocess.popen directly</a><a class="headerlink" href="#call-subprocess-popen-directly" title="Permalink to this heading">¶</a></h4>
<p>Calling subprocess.popen is often simple and good. For example, the following executes the ‘npm run dev’ command in a given directory.  Leo continues, without waiting for the command to return:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="n">base_dir</span><span class="p">)</span>
<span class="n">subprocess</span><span class="o">.</span><span class="n">Popen</span><span class="p">(</span><span class="s1">&#39;npm run dev&#39;</span><span class="p">,</span> <span class="n">shell</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</pre></div>
</div>
<p>The following hangs Leo until the command completes:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="n">base_dir</span><span class="p">)</span>
<span class="n">proc</span> <span class="o">=</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">Popen</span><span class="p">(</span><span class="n">command</span><span class="p">,</span> <span class="n">shell</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">proc</span><span class="o">.</span><span class="n">communicate</span><span class="p">()</span>
</pre></div>
</div>
<p><strong>Note</strong>: ‘cd directory’ does not seem to work when run using subprocess.popen on Windows 10.</p>
</section>
<section id="call-g-execute-shell-commands">
<h4><a class="toc-backref" href="#id23">Call g.execute_shell_commands</a><a class="headerlink" href="#call-g-execute-shell-commands" title="Permalink to this heading">¶</a></h4>
<p>Use g.execute_shell_commands is a thin wrapper around subprocess.popen.  It calls subprocess.popen once for every command in a list.  This function waits for commands that start with ‘&amp;’. Here it is:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">execute_shell_commands</span><span class="p">(</span><span class="n">commands</span><span class="p">,</span> <span class="n">trace</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">g</span><span class="o">.</span><span class="n">isString</span><span class="p">(</span><span class="n">commands</span><span class="p">):</span> <span class="n">commands</span> <span class="o">=</span> <span class="p">[</span><span class="n">commands</span><span class="p">]</span>
    <span class="k">for</span> <span class="n">command</span> <span class="ow">in</span> <span class="n">commands</span><span class="p">:</span>
        <span class="n">wait</span> <span class="o">=</span> <span class="ow">not</span> <span class="n">command</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s1">&#39;&amp;&#39;</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">command</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s1">&#39;&amp;&#39;</span><span class="p">):</span> <span class="n">command</span> <span class="o">=</span> <span class="n">command</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
        <span class="k">if</span> <span class="n">trace</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&gt;</span><span class="si">%s%s</span><span class="se">\n</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="s1">&#39;&#39;</span> <span class="k">if</span> <span class="n">wait</span> <span class="k">else</span> <span class="s1">&#39;&amp;&#39;</span><span class="p">,</span> <span class="n">command</span><span class="p">))</span>
        <span class="n">proc</span> <span class="o">=</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">Popen</span><span class="p">(</span><span class="n">command</span><span class="p">,</span> <span class="n">shell</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">wait</span><span class="p">:</span> <span class="n">proc</span><span class="o">.</span><span class="n">communicate</span><span class="p">()</span>
</pre></div>
</div>
<p>For example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="n">base_dir</span><span class="p">)</span>
<span class="n">g</span><span class="o">.</span><span class="n">execute_shell_commands</span><span class="p">([</span><span class="s1">&#39;&amp;npm run dev&#39;</span><span class="p">,])</span>
</pre></div>
</div>
</section>
<section id="call-g-execute-shell-commands-with-options">
<h4><a class="toc-backref" href="#id24">Call g.execute_shell_commands_with_options</a><a class="headerlink" href="#call-g-execute-shell-commands-with-options" title="Permalink to this heading">¶</a></h4>
<p>g.execute_shell_commands_with_options is more flexible.  It allows scripts to get both the starting directory and the commands themselves from Leo’s settings. Its signature is:</p>
<dl>
<dt>def execute_shell_commands_with_options(</dt><dd><p>base_dir = None,
c = None,
command_setting = None,
commands = None,
path_setting = None,
warning = None,</p>
</dd>
<dt>):</dt><dd><p>‘’’
A helper for prototype commands or any other code that
runs programs in a separate process.</p>
<p>base_dir:           Base directory to use if no path_setting is given.
commands:           A list of commands, for g.execute_shell_commands.
commands_setting:   Name of &#64;data setting for commands.
path_setting:       Name of &#64;string setting for the base directory.
warning:            A warning to be printed before executing the commands.
‘’’</p>
</dd>
</dl>
<p>For example, put this in myLeoSettings.leo:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@data</span> <span class="n">my</span><span class="o">-</span><span class="n">npm</span><span class="o">-</span><span class="n">commands</span>
<span class="n">yarn</span> <span class="n">dev</span>

<span class="nd">@string</span> <span class="n">my</span><span class="o">-</span><span class="n">npm</span><span class="o">-</span><span class="n">base</span> <span class="o">=</span> <span class="o">/</span><span class="n">npmtest</span>
</pre></div>
</div>
<p>And then run:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">g</span><span class="o">.</span><span class="n">execute_shell_commands_with_options</span><span class="p">(</span>
    <span class="n">c</span> <span class="o">=</span> <span class="n">c</span><span class="p">,</span>
    <span class="n">command_setting</span> <span class="o">=</span> <span class="s1">&#39;my-npm-commands&#39;</span><span class="p">,</span>
    <span class="n">path_setting</span><span class="o">=</span> <span class="s1">&#39;my-npm-base&#39;</span><span class="p">,</span>
<span class="p">)</span>
</pre></div>
</div>
</section>
</section>
<section id="using-g-app-backgroundprocessmanager">
<h3><a class="toc-backref" href="#id25">Using g.app.backgroundProcessManager</a><a class="headerlink" href="#using-g-app-backgroundprocessmanager" title="Permalink to this heading">¶</a></h3>
<p>g.app.backgroundProcessManager is the singleton instance of the BackgroundProcessManager (BPM) class. This class runs background processes, <em>without blocking Leo</em>. The BPM manages a queue of processes, and runs them <em>one at a time</em> so that their output remains separate.</p>
<p>BPM.start_process(c, command, kind, fn=None, shell=False) adds a process to the queue that will run the given command:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">bpm</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">backgroundProcessManager</span>
<span class="n">bpm</span><span class="o">.</span><span class="n">start_process</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">command</span><span class="o">=</span><span class="s1">&#39;ls&#39;</span><span class="p">,</span> <span class="n">kind</span><span class="o">=</span><span class="s1">&#39;ls&#39;</span><span class="p">,</span> <span class="n">shell</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</pre></div>
</div>
<p>BM.kill(kind=None) kills all process with the given kind. If kind is None or ‘all’, all processes are killed.</p>
<p>You can add processes to the queue at any time. For example, you can rerun the ‘pylint’ command while a background process is running.</p>
<p>The BackgroundProcessManager is completely safe: all of its code runs in the main process.</p>
<p><strong>Running multiple processes simultaneously</strong></p>
<p>Only one process at a time should be producing output. All processes that <em>do</em> produce output should be managed by the singleton BPM instance.</p>
<p>To run processes that <em>don’t</em> produce output, just call subprocess.Popen:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">subprocess</span>
<span class="n">subprocess</span><span class="o">.</span><span class="n">Popen</span><span class="p">(</span><span class="s1">&#39;ls&#39;</span><span class="p">,</span> <span class="n">shell</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</pre></div>
</div>
<p>You can run as many of these process as you like, without involving the BPM in any way</p>
</section>
<section id="using-g-execute-shell-commands">
<h3><a class="toc-backref" href="#id26">Using g.execute_shell_commands</a><a class="headerlink" href="#using-g-execute-shell-commands" title="Permalink to this heading">¶</a></h3>
<p>g.execute_shell_command tales a single argument, which may be either a string or a list of strings. Each string represents one command. g.execute_shell_command executes each command in order.  Commands starting with ‘&amp;’ are executed without waiting. Commands that do not start with ‘&amp;’ finish before running later commands. Examples:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Run the qml app in a separate process:</span>
<span class="n">g</span><span class="o">.</span><span class="n">execute_shell_commands</span><span class="p">(</span><span class="s1">&#39;qmlscene /test/qml_test.qml&#39;</span><span class="p">)</span>

<span class="c1"># List the contents of a directory:</span>
<span class="n">g</span><span class="o">.</span><span class="n">execute_shell_commands</span><span class="p">([</span>
    <span class="s1">&#39;cd ~/test&#39;</span><span class="p">,</span>
    <span class="s1">&#39;ls -a&#39;</span><span class="p">,</span>
<span class="p">])</span>

<span class="c1"># Run a python test in a separate process.</span>
<span class="n">g</span><span class="o">.</span><span class="n">execute_shell_commands</span><span class="p">(</span><span class="s1">&#39;python /test/qt_test.py&#39;</span><span class="p">)</span>
</pre></div>
</div>
<p>g.execute_shell_commands_with_options inits an environment and then calls g.execute_shell_commands.  See Leo’s source code for details.</p>
</section>
</section>
<section id="running-leo-in-batch-mode">
<h2><a class="toc-backref" href="#id27">Running Leo in batch mode</a><a class="headerlink" href="#running-leo-in-batch-mode" title="Permalink to this heading">¶</a></h2>
<p>On startup, Leo looks for two arguments of the form:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">--</span><span class="n">script</span> <span class="n">scriptFile</span>
</pre></div>
</div>
<p>If found, Leo enters batch mode. In batch mode Leo does not show any windows. Leo assumes the scriptFile contains a Python script and executes the contents of that file using Leo’s Execute Script command. By default, Leo sends all output to the console window. Scripts in the scriptFile may disable or enable this output by calling app.log.disable or app.log.enable</p>
<p>Scripts in the scriptFile may execute any of Leo’s commands except the Edit Body and Edit Headline commands. Those commands require interaction with the user. For example, the following batch script reads a Leo file and prints all the headlines in that file:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">path</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">os_path_finalize_join</span><span class="p">(</span><span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">loadDir</span><span class="p">,</span><span class="s1">&#39;..&#39;</span><span class="p">,</span><span class="s1">&#39;test&#39;</span><span class="p">,</span><span class="s1">&#39;test.leo&#39;</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">g</span><span class="o">.</span><span class="n">os_path_exists</span><span class="p">(</span><span class="n">path</span><span class="p">),</span><span class="n">path</span>

<span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">disable</span><span class="p">()</span> <span class="c1"># disable reading messages while opening the file</span>
<span class="n">c2</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">openWithFileName</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
<span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">enable</span><span class="p">()</span> <span class="c1"># re-enable the log.</span>

<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">c2</span><span class="o">.</span><span class="n">all_positions</span><span class="p">():</span>
    <span class="n">g</span><span class="o">.</span><span class="n">es</span><span class="p">(</span><span class="n">g</span><span class="o">.</span><span class="n">toEncodedString</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">h</span><span class="p">,</span><span class="s2">&quot;utf-8&quot;</span><span class="p">))</span>
</pre></div>
</div>
</section>
<section id="using-pyplot-and-matplotlib">
<h2><a class="toc-backref" href="#id28">Using &#64;pyplot and matplotlib</a><a class="headerlink" href="#using-pyplot-and-matplotlib" title="Permalink to this heading">¶</a></h2>
<section id="overview">
<h3><a class="toc-backref" href="#id29">Overview</a><a class="headerlink" href="#overview" title="Permalink to this heading">¶</a></h3>
<p>Leo’s &#64;pyplot nodes support
<a class="reference external" href="https://matplotlib.org/users/index.html">matplotlib</a>.
&#64;pyplot nodes start with &#64;pyplot in the headline.  The rest of the headline is comments. These nodes should contain matplotlib scripts that create figures or animations.  Like this:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">fig2</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">figure</span><span class="p">()</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="o">-</span><span class="mi">9</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="o">-</span><span class="mi">9</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span><span class="o">.</span><span class="n">reshape</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">base</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">hypot</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
<span class="n">images</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">add</span> <span class="ow">in</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">15</span><span class="p">):</span>
    <span class="n">images</span><span class="o">.</span><span class="n">append</span><span class="p">((</span>
        <span class="n">plt</span><span class="o">.</span><span class="n">pcolor</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">base</span><span class="o">+</span><span class="n">add</span><span class="p">,</span> <span class="n">norm</span><span class="o">=</span><span class="n">plt</span><span class="o">.</span><span class="n">Normalize</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">30</span><span class="p">)),</span>
    <span class="p">))</span>
<span class="n">animation</span> <span class="o">=</span> <span class="n">animation</span><span class="o">.</span><span class="n">ArtistAnimation</span><span class="p">(</span><span class="n">fig2</span><span class="p">,</span> <span class="n">images</span><span class="p">,</span>
    <span class="n">interval</span><span class="o">=</span><span class="mi">50</span><span class="p">,</span> <span class="n">repeat_delay</span><span class="o">=</span><span class="mi">3000</span><span class="p">,</span> <span class="n">blit</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">permanentScriptDict</span><span class="p">[</span><span class="s1">&#39;animations&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">animation</span>
    <span class="c1"># Keep a python reference to the animation, so it will complete.</span>
</pre></div>
</div>
<p><strong>Notes</strong></p>
<ol class="arabic">
<li><p>If the viewrendered (VR) pane is open, Leo will display the animation in the VR pane whenever the user selects the &#64;pyplot node.  This has been tested only with the viewrendered.py, not the viewrendered2.py plugin.</p></li>
<li><p>In addition to c, g, and p, the VR code predefines several other vars.  The VR code does the following imports:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">matplotlib</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="nn">plt</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
<span class="kn">import</span> <span class="nn">matplotlib.animation</span> <span class="k">as</span> <span class="nn">animation</span>
</pre></div>
</div>
</li>
</ol>
<p>and then predefines the animation, matplotlib, np, numpy and plt vars in the &#64;pyplot script. numpy is also predefined as an alias for np.</p>
</section>
<section id="displaying-images-externally">
<h3><a class="toc-backref" href="#id30">Displaying images externally</a><a class="headerlink" href="#displaying-images-externally" title="Permalink to this heading">¶</a></h3>
<p>To display images and animations in an external window, <em>don’t</em> put the script in an &#64;pyplot node. Instead, put the script in a regular node, with the following modifications:</p>
<ol class="arabic">
<li><p>Add the required imports:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="nn">plt</span>
<span class="kn">import</span> <span class="nn">matplotlib.animation</span> <span class="k">as</span> <span class="nn">animation</span>
</pre></div>
</div>
</li>
<li><p>Place the following at the end of the script:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">plt</span><span class="o">.</span><span class="n">ion</span><span class="p">()</span>
    <span class="c1"># sets interactive mode. Prevents this message:</span>
    <span class="c1"># QCoreApplication::exec: The event loop is already running</span>
<span class="n">plt</span><span class="o">.</span><span class="n">show</span><span class="p">()</span>
</pre></div>
</div>
</li>
</ol>
<p><strong>Bugs</strong></p>
<ol class="arabic simple">
<li><p>Once you use the VR pane to display an image, you can’t (at present) display an image externally.</p></li>
<li><p>The VR plugin will refuse to close the VR pane if it ever displays an &#64;pyplot image or animation. This prevents Leo from hard crashing in the pyplot code. As a workaround, you can  resize the VR pane so it is effectively hidden.</p></li>
</ol>
</section>
</section>
<section id="working-with-directives-and-paths">
<h2><a class="toc-backref" href="#id31">Working with directives and paths</a><a class="headerlink" href="#working-with-directives-and-paths" title="Permalink to this heading">¶</a></h2>
<p>Scripts can easily determine what directives are in effect at a particular position in an outline. c.scanAllDirectives(p) returns a Python dictionary whose keys are directive names and whose values are the value in effect at position p. For example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">d</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">scanAllDirectives</span><span class="p">(</span><span class="n">p</span><span class="p">)</span> <span class="n">g</span><span class="o">.</span><span class="n">es</span><span class="p">(</span><span class="n">g</span><span class="o">.</span><span class="n">dictToString</span><span class="p">(</span><span class="n">d</span><span class="p">))</span>
</pre></div>
</div>
<p>In particular, d.get(‘path’) returns the full, absolute path created by all &#64;path directives that are in ancestors of node p. If p is any kind of &#64;file node (including &#64;file, &#64;auto, &#64;clean, etc.), the following script will print the full path to the created file:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">path</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">)</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">anyAtFileNodeName</span><span class="p">()</span>
<span class="k">if</span> <span class="n">name</span><span class="p">:</span>
   <span class="n">name</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">os_path_finalize_join</span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="n">name</span><span class="p">)</span>
   <span class="n">g</span><span class="o">.</span><span class="n">es</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="writing-g-es-output-to-other-tabs">
<h2><a class="toc-backref" href="#id32">Writing g.es output to other tabs</a><a class="headerlink" href="#writing-g-es-output-to-other-tabs" title="Permalink to this heading">¶</a></h2>
<p>g.es can send it’s output to tabs other than the log tab:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">c</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">selectTab</span><span class="p">(</span><span class="s1">&#39;Test&#39;</span><span class="p">)</span>
    <span class="c1"># Create Test or make it visible.</span>

<span class="n">g</span><span class="o">.</span><span class="n">es</span><span class="p">(</span><span class="s1">&#39;Test&#39;</span><span class="p">,</span><span class="n">color</span><span class="o">=</span><span class="s1">&#39;blue&#39;</span><span class="p">,</span><span class="n">tabName</span><span class="o">=</span><span class="s1">&#39;Test&#39;</span><span class="p">)</span>
    <span class="c1"># Write to the test tab.</span>
</pre></div>
</div>
<p>Plugins and scripts may call c.frame.log.createTab to create non-text widgets in the log pane:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">leo.core.leoQt</span> <span class="kn">import</span> <span class="n">QtWidgets</span>
<span class="n">log</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">log</span>
<span class="n">w</span> <span class="o">=</span> <span class="n">log</span><span class="o">.</span><span class="n">createTab</span><span class="p">(</span><span class="s1">&#39;My Tab&#39;</span><span class="p">,</span> <span class="n">createText</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">widget</span><span class="o">=</span><span class="n">QtWidgets</span><span class="o">.</span><span class="n">QFrame</span><span class="p">())</span>
<span class="n">log</span><span class="o">.</span><span class="n">selectTab</span><span class="p">(</span><span class="s1">&#39;My Tab&#39;</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="writing-clickable-links-to-the-log-tab">
<h2><a class="toc-backref" href="#id33">Writing clickable links to the Log tab</a><a class="headerlink" href="#writing-clickable-links-to-the-log-tab" title="Permalink to this heading">¶</a></h2>
<p>g.es_clickable_link(c, p, line_number, message) writes a clickable
message to the Log pane.</p>
<p>Clicking it selects the given line number of p.b.</p>
</section>
</section>


            <div class="clearer"></div>
          </div>
        </div>
      </div>
      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
        <div class="sphinxsidebarwrapper">
            <p class="logo"><a href="leo_toc.html">
              <img class="logo" src="_static/LeoLogo.svg" alt="Logo"/>
            </a></p>
  <div>
    <h4>Previous topic</h4>
    <p class="topless"><a href="console-gui.html"
                          title="previous chapter">Leo’s Console Gui</a></p>
  </div>
  <div>
    <h4>Next topic</h4>
    <p class="topless"><a href="theory.html"
                          title="next chapter">Exploring Leo’s Code Base</a></p>
  </div>
<div id="searchbox" style="display: none" role="search">
  <h3 id="searchlabel">Quick search</h3>
    <div class="searchformwrapper">
    <form class="search" action="search.html" method="get">
      <input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
      <input type="submit" value="Go" />
    </form>
    </div>
</div>
<script>document.getElementById('searchbox').style.display = "block"</script>
        </div>
<div id="sidebarbutton" title="Collapse sidebar">
<span>«</span>
</div>

      </div>
      <div class="clearer"></div>
    </div>
    <div class="related" role="navigation" aria-label="related navigation">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="genindex.html" title="General Index"
             >index</a></li>
        <li class="right" >
          <a href="theory.html" title="Exploring Leo’s Code Base"
             >next</a> |</li>
        <li class="right" >
          <a href="console-gui.html" title="Leo’s Console Gui"
             >previous</a> |</li>
        <li class="nav-item nav-item-0"><a href="leo_toc.html">Leo 6.7.2 documentation</a> &#187;</li>
          <li class="nav-item nav-item-1"><a href="intermediatetopics.html" >Advanced Topics</a> &#187;</li>
        <li class="nav-item nav-item-this"><a href="">A Miscellany of Leo Scripting</a></li> 
      </ul>
    </div>
    <div class="footer" role="contentinfo">
        &#169; Copyright 1997-2023, Edward K. Ream.
      Last updated on February 28, 2023.
      Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 5.3.0.
    </div>
  </body>
</html>